home *** CD-ROM | disk | FTP | other *** search
/ NeXT Education Software Sampler 1992 Fall / NeXT Education Software Sampler 1992 Fall.iso / Programming / Source / plot3d / Plot3DView.m < prev    next >
Encoding:
Text File  |  1992-08-18  |  13.0 KB  |  595 lines

  1. /* Plot3DView.m   Copyright 1992 Steve Ludtke */
  2. /* This view allows the display of 3d functions with real time rotation */
  3.  
  4. #import "Plot3DView.h"
  5. #import "PControl.h"
  6. #import <appkit/Control.h>
  7. #import <appkit/Matrix.h>
  8. #import <appkit/Application.h>
  9. #import <appkit/NXColorWell.h>
  10. #import <appkit/NXColorPanel.h>
  11. #import <appkit/PrintInfo.h>
  12. #import <dpsclient/event.h>
  13. #import <dpsclient/psops.h>
  14. #import <appkit/color.h>
  15. #import <appkit/OpenPanel.h>
  16. #import <stdio.h>
  17. #import <math.h>
  18. #include <libc.h>
  19. #import "Expression.h"
  20. #import "DensView.h"
  21.  
  22. void PSsethel();    /* makes Helvetica current font */
  23.  
  24. /* drawing and transformation macros */
  25. #define MOVETO(x,y) pscom[psc]=dps_moveto; pspath[psc*2]=x; pspath[psc*2+1]=y; psc++ 
  26. #define LINETO(x,y) pscom[psc]=dps_lineto; pspath[psc*2]=x; pspath[psc*2+1]=y; psc++
  27. #define XFORM() x1=mx[0]*x+mx[1]*y+mx[2]*z; y1=mx[3]*x+mx[4]*y+mx[5]*z; z1=mx[6]*x+mx[7]*y+mx[8]*z
  28.  
  29. extern id NXApp;
  30.  
  31. @implementation Plot3DView
  32. -initFrame:(NXRect *)myrect
  33. {
  34. int i;
  35. [super initFrame:(NXRect *)myrect];
  36. /* origin is in the center of the view, and size is +-1 + .1 for border */
  37. [self setDrawSize:2.2 :2.2];
  38. [self setDrawOrigin:-1.1 :-1.1];
  39.  
  40. /* bounding box for drawing */
  41. psbbox[0]=psbbox[1]=-1.1;
  42. psbbox[2]=psbbox[3]=1.1;
  43.  
  44. /* initialize preferences */
  45. for (i=0; i<MAXSETS; i++) {
  46.     pref[i].ndata=0; 
  47.     pref[i].sym= -1;
  48.     pref[i].color=NXConvertGrayToColor(0.0);
  49.     pref[i].fileData=NULL;
  50.     pref[i].expr=[[Expression alloc] init];
  51.     [pref[i].expr parse:"0"];
  52. }
  53. /* initial function to display */
  54. [pref[0].expr parse:"cos(sqrt(x^2+y^2))"];
  55. pref[0].sym=6;
  56.  
  57. /* start spinning, all angles in radians */
  58. chi=.4;
  59. theta=.5236;
  60. dchi=0.025;
  61. Tgrids=20;
  62. initflag=1;
  63.  
  64. [[[NXApp printInfo] setHorizCentered:YES] setOrientation:NX_LANDSCAPE andAdjust:YES];
  65.  
  66. timer=(DPSTimedEntry)DPSAddTimedEntry(TIMESTEP,itstime,self,NX_RUNMODALTHRESHOLD);
  67.  
  68. return self;
  69. }
  70.  
  71. /* fix the scaling after being resized */
  72. -superviewSizeChanged:(const NXSize *)oldsize
  73. {
  74. [super superviewSizeChanged:oldsize];
  75. [self setDrawSize:2.2 :2.2];
  76. [self setDrawOrigin:-1.1 :-1.1];
  77. [self zoom:self];
  78. return self;
  79. }
  80.  
  81. -free
  82. {
  83. DPSRemoveTimedEntry(timer);
  84. [super free];
  85. return self;
  86. }
  87.  
  88. /* please note that XFORM,MOVETO, and DRAWTO are macros, not functions */
  89. /* when used, curly braces are necessary, since they are absent in the */
  90. /* macro definition. */
  91. -drawSelf:(NXRect *)myrect :(int)rectCount
  92. {
  93. static float mx[9];
  94. char s[30];
  95. float x,y,z,x1,y1,z1;
  96. int i,j,k,fl,n;
  97.  
  98. n=Tgrids;
  99.  
  100. PSsetlinewidth(0.003);        /* not 0 since printer can't handle it */
  101.                 /* I'll come up with something better later */
  102.                 
  103. /* calculate transformation matrix */
  104. mx[0]=cos(chi);            /* theta=alt, chi=az  rotation matrix */
  105. mx[1]= -cos(theta)*sin(chi);
  106. mx[2]=sin(theta)*sin(chi);
  107. mx[3]=sin(chi);
  108. mx[4]=cos(theta)*cos(chi);
  109. mx[5]= -sin(theta)*cos(chi);
  110. mx[6]=0.0;
  111. mx[7]=sin(theta);
  112. mx[8]=cos(theta);
  113.  
  114. PSsetgray(NX_WHITE);        /* clear view */
  115. NXRectFill(&bounds);
  116.  
  117. /* draw top and bottom of viewing area */
  118. PSsetgray(.3333);
  119. x=y=z=-0.5;
  120. XFORM();
  121. PSmoveto(x1,z1);
  122. x=0.5;
  123. XFORM();
  124. PSlineto(x1,z1);
  125. y=0.5;
  126. XFORM();
  127. PSlineto(x1,z1);
  128. x=-0.5;
  129. XFORM();
  130. PSlineto(x1,z1);
  131. y=-0.5;
  132. XFORM();
  133. PSlineto(x1,z1);
  134. z=.5;
  135. XFORM();
  136. PSmoveto(x1,z1);
  137. x=0.5;
  138. XFORM();
  139. PSlineto(x1,z1);
  140. y=0.5;
  141. XFORM();
  142. PSlineto(x1,z1);
  143. x=-0.5;
  144. XFORM();
  145. PSlineto(x1,z1);
  146. y=-0.5;
  147. XFORM();
  148. PSlineto(x1,z1);
  149. PSstroke();
  150.  
  151. psc=0;
  152.  
  153. /* plot the data */
  154. for (i=0; i<MAXSETS; i++) {
  155.     PSsetgray(NXGrayComponent(pref[i].color));
  156.     if (pref[i].sym==-1) continue;
  157.     switch(pref[i].sym) {
  158.         case 0:        /* dot */
  159.         for (j=0; j<pref[i].ndata; j++) {
  160.             x=pref[i].data[j].x;
  161.             y=pref[i].data[j].y;
  162.             z=pref[i].data[j].z;
  163.             XFORM();
  164.             MOVETO(x1,z1);
  165.             LINETO(x1,z1);
  166.             if (psc>950) {
  167.                 DPSDoUserPath(pspath,psc*2,dps_float,pscom,psc,psbbox,dps_ustroke);
  168.                 psc=0;
  169.             }
  170.         }
  171.         break;
  172.         case 1:        /* + */
  173.         for (j=0; j<pref[i].ndata; j++) {
  174.             x=pref[i].data[j].x;
  175.             y=pref[i].data[j].y;
  176.             z=pref[i].data[j].z;
  177.             XFORM();
  178.             MOVETO(x1-SS,z1);
  179.             LINETO(x1+SS,z1);
  180.             MOVETO(x1,z1-SS);
  181.             LINETO(x1,z1+SS);
  182.             if (psc>950) {
  183.                 DPSDoUserPath(pspath,psc*2,dps_float,pscom,psc,psbbox,dps_ustroke);
  184.                 psc=0;
  185.             }
  186.         }
  187.         break;
  188.         case 2:        /* triangle */
  189.         for (j=0; j<pref[i].ndata; j++) {
  190.             x=pref[i].data[j].x;
  191.             y=pref[i].data[j].y;
  192.             z=pref[i].data[j].z;
  193.             XFORM();
  194.             MOVETO(x1-SS,z1-SS*.5773);
  195.             LINETO(x1+SS,z1-SS*.5773);
  196.             LINETO(x1,z1+SS*1.1547);
  197.             LINETO(x1-SS,z1-SS*.5773);
  198.             if (psc>950) {
  199.                 DPSDoUserPath(pspath,psc*2,dps_float,pscom,psc,psbbox,dps_ustroke);
  200.                 psc=0;
  201.             }
  202.         }
  203.         break;
  204.         case 3:        /* box */
  205.         for (j=0; j<pref[i].ndata; j++) {
  206.             x=pref[i].data[j].x;
  207.             y=pref[i].data[j].y;
  208.             z=pref[i].data[j].z;
  209.             XFORM();
  210.             MOVETO(x1-SS,z1-SS);
  211.             LINETO(x1+SS,z1-SS);
  212.             LINETO(x1+SS,z1+SS);
  213.             LINETO(x1-SS,z1+SS);
  214.             LINETO(x1-SS,z1-SS);
  215.             if (psc>950) {
  216.                 DPSDoUserPath(pspath,psc*2,dps_float,pscom,psc,psbbox,dps_ustroke);
  217.                 psc=0;
  218.             }
  219.         }
  220.         break;
  221.         case 4:        /* X */
  222.         for (j=0; j<pref[i].ndata; j++) {
  223.             x=pref[i].data[j].x;
  224.             y=pref[i].data[j].y;
  225.             z=pref[i].data[j].z;
  226.             XFORM();
  227.             MOVETO(x1-SS,z1-SS);
  228.             LINETO(x1+SS,z1+SS);
  229.             MOVETO(x1-SS,z1+SS);
  230.             LINETO(x1+SS,z1-SS);
  231.             if (psc>950) {
  232.                 DPSDoUserPath(pspath,psc*2,dps_float,pscom,psc,psbbox,dps_ustroke);
  233.                 psc=0;
  234.             }
  235.         }
  236.         break;
  237.         case 5:        /* 1/2 mesh */
  238.         if (pref[i].fileData==NULL) fl=1;
  239.         else fl=0;
  240.         for (j=0; j<pref[i].ndata; j++) {
  241.             x=pref[i].data[j].x;
  242.             y=pref[i].data[j].y;
  243.             z=pref[i].data[j].z;
  244.             XFORM();
  245.             if (j==0 || (x<pref[i].data[j-1].x&&pref[i].data[j-2].y==pref[i].data[j-1].y)) { MOVETO(x1,z1); }
  246.             else { LINETO(x1,z1); }
  247.             if (psc>950) {
  248.                 DPSDoUserPath(pspath,psc*2,dps_float,pscom,psc,psbbox,dps_ustroke);
  249.                 psc=0;
  250.                 MOVETO(x1,z1);
  251.             }
  252.         }
  253.         break;
  254.         case 6:        /* mesh */
  255.         if (pref[i].fileData!=NULL) break;
  256.         for (j=0; j<pref[i].ndata; j++) {
  257.             x=pref[i].data[j].x;
  258.             y=pref[i].data[j].y;
  259.             z=pref[i].data[j].z;
  260.             XFORM();
  261.             if (j%n==0) { MOVETO(x1,z1); }
  262.             else { LINETO(x1,z1); }
  263.             if (psc>950) {
  264.                 DPSDoUserPath(pspath,psc*2,dps_float,pscom,psc,psbbox,dps_ustroke);
  265.                 psc=0;
  266.                 MOVETO(x1,z1);
  267.             }
  268.         }
  269.         for (j=0; j<n; j++) {
  270.             for (k=j; k<pref[i].ndata; k+=n) {
  271.                 x=pref[i].data[k].x;
  272.                 y=pref[i].data[k].y;
  273.                 z=pref[i].data[k].z;
  274.                 XFORM();
  275.                 if (k==j) { MOVETO(x1,z1); }
  276.                 else { LINETO(x1,z1); }
  277.                     if (psc>950) {
  278.                 DPSDoUserPath(pspath,psc*2,dps_float,pscom,psc,psbbox,dps_ustroke);
  279.                     psc=0;
  280.                     MOVETO(x1,z1);
  281.                 }
  282.             }
  283.         }
  284.         break;
  285.     }
  286.     if (psc!=0) {
  287.         DPSDoUserPath(pspath,psc*2,dps_float,pscom,psc,psbbox,dps_ustroke);
  288.         psc=0;
  289.     }
  290. }
  291.  
  292. /* display alt and az in upper right corner */
  293. PSsetgray(NX_BLACK);
  294. PSsethel();
  295. sprintf(s,"Alt:%6.2f  Az:%6.2f",theta*57.295787,chi*57.295787);
  296. PSmoveto(.6,1.05);
  297. PSshow(s);
  298. PSstroke();
  299.  
  300. return self;
  301. }    
  302.  
  303. /* recalculates and redisplays data */
  304. -zoom:sender
  305. {
  306. float x0,x1,xs,y0,y1,ys;
  307. float z0,z1,x,y,zz;
  308. int i,j=0,k,n;
  309. id tmp;
  310.  
  311. /* calculate minima, maxima and step sizes */
  312. x0=minX;
  313. x1=maxX;
  314. y0=minY;
  315. y1=maxY;
  316.  
  317. Tgrids=n=[grids intValue];
  318. if (n<=0||n>=60) { [grids setIntValue:20]; n=20; return self; }
  319. xs=(x1-x0)/(float)n;
  320. ys=(y1-y0)/(float)n;
  321. x1-=xs/2.0;
  322. y1-=ys/2.0;
  323.  
  324. /* make sure there's enough space in the data array */
  325. for (i=0; i<NFN; i++) {
  326.     if (pref[i].sym==-1 || pref[i].fileData!=NULL) continue;
  327.     if (pref[i].ndata!=(n*n)) {
  328.         if (pref[i].ndata!=0) free(pref[i].data);
  329.         pref[i].data=malloc(sizeof(Point)*(n*n+2));
  330.         pref[i].ndata=n*n;
  331.     }
  332. }
  333. z0=MAXFLOAT;
  334. z1= -MAXFLOAT;
  335.  
  336. /* calculate (function mode) or clip (file mode) the data */
  337. /* also finds min/max Z */
  338. for (i=0; i<NFN; i++) {
  339.     if (pref[i].sym==-1) continue;
  340.     tmp=pref[i].expr;
  341.     if (pref[i].fileData!=NULL) {
  342.         k=0;
  343.         for (j=0; j<pref[i].nfdata; j++) {
  344.             if (pref[i].fileData[j].x>x1||pref[i].fileData[j].x<x0||
  345.                 pref[i].fileData[j].y>y1||pref[i].fileData[j].y<y0) continue;
  346.             pref[i].data[k].x=(pref[i].fileData[j].x-x0)/(x1-x0)-.5;
  347.             [tmp setVar:"x" value:pref[i].data[k].x];
  348.             pref[i].data[k].y=(pref[i].fileData[j].y-y0)/(y1-y0)-.5;
  349.             [tmp setVar:"y" value:pref[i].data[k].y];
  350.             [tmp setVar:"z" value:pref[i].fileData[j].z];
  351.             pref[i].data[k].z=[tmp resultValue];
  352.             if (pref[i].data[k].z>z1) z1=pref[i].data[k].z;
  353.             if (pref[i].data[k].z<z0) z0=pref[i].data[k].z;
  354.             k++;
  355.         }
  356.         pref[i].ndata=k;
  357.         continue;
  358.     }
  359.     
  360.     j=0;
  361.     for (y=y0; y<y1; y+=ys) {
  362.         for (x=x0; x<x1; x+=xs) {
  363.             pref[i].data[j].x=(x-x0)/(x1-x0)-.5;
  364.             pref[i].data[j].y=(y-y0)/(y1-y0)-.5;
  365.             [tmp setVar:"x" value:x];
  366.             [tmp setVar:"y" value:y];
  367.             zz=pref[i].data[j].z=[tmp resultValue];
  368.             if (zz!=zz || zz<-MAXFLOAT || zz>MAXFLOAT) zz=pref[i].data[j].z=MAXFLOAT;
  369.             if (zz>z1) z1=zz;
  370.             if (zz<z0) z0=zz;
  371.             j++;
  372.         }
  373.     }
  374. }
  375.  
  376. if (z1<=z0) { z1=z0+.001; z0-=.001; }
  377. /* allow the controller to override min/max Z */
  378. [controller minmaxZ:&z0 :&z1];
  379.  
  380. /* scale and clip Z */
  381. for (i=0; i<NFN; i++) {
  382.     if (pref[i].sym==-1) continue;
  383.     for (k=0; k<pref[i].ndata; k++) {
  384.         if (pref[i].data[k].z>z1) pref[i].data[k].z=z1;
  385.         if (pref[i].data[k].z<z0) pref[i].data[k].z=z0;
  386.         pref[i].data[k].z=(pref[i].data[k].z-z0)/(z1-z0)-.5;
  387.     }
  388. }
  389.  
  390. /* display 3d plot */
  391. [self display];
  392. /* display density plot */
  393. [controller updDen];
  394. return self;
  395. }
  396.  
  397. /* obvious */
  398. -setAng:(float)az :(float)alt
  399. {
  400. theta=alt;
  401. chi=az;
  402. return self;
  403. }
  404.  
  405. /* allows spinning and zooming with the mouse */
  406. -mouseDown:(NXEvent *)oevent 
  407. {
  408. int oldMask,loop=1;
  409. float ix=0,iy=0,ix1=0,iy1=0,ix2=0,iy2=0,ix3=0,iy3=0;
  410. float mx[9],t;
  411. NXEvent *event,evs;
  412. long tm,tm2;
  413.  
  414. evs=*oevent;
  415. oevent=&evs;
  416. [self convertPoint:&oevent->location fromView:nil];
  417. ix2=ix=oevent->location.x;
  418. iy2=iy=oevent->location.y;
  419. tm2=tm=oevent->time;
  420.  
  421. if (zMode) {
  422.     mx[0]=cos(chi)/2.0;        /* theta=alt, chi=az  rotation matrix */
  423.     mx[1]= -cos(theta)*sin(chi)/2.0;
  424.     mx[2]=sin(theta)*sin(chi);
  425.     mx[3]=sin(chi);
  426.     mx[4]=cos(theta)*cos(chi);
  427.     mx[5]= -sin(theta)*cos(chi);
  428.     mx[6]=0.0;
  429.     mx[7]=sin(theta)/2.0;
  430.     mx[8]=cos(theta);
  431.     ix=mx[0]*ix2+mx[1]*iy2+.5*mx[2];
  432.     iy=mx[6]*ix2+mx[7]*iy2+.5*mx[8];
  433.     ix1=ix2=ix3=ix;
  434.     iy1=iy2=iy3=iy;
  435. }
  436.  
  437. oldMask = [window addToEventMask:NX_LMOUSEDRAGGEDMASK];
  438.  
  439. while (loop) {
  440.     event = [NXApp getNextEvent:(NX_LMOUSEUPMASK | NX_LMOUSEDRAGGEDMASK)];
  441.     [self convertPoint:&event->location fromView:nil];
  442.  
  443.     if (zMode) {
  444.         switch (event->type) {
  445.         case NX_LMOUSEUP:
  446.                 loop = 0;
  447.         ix=minX;
  448.         iy=minY;
  449.         ix1=maxX-ix;
  450.         iy1=maxY-iy;
  451.         ix2=oevent->location.x;
  452.         iy2=oevent->location.y;
  453.         ix3=event->location.x;
  454.         iy3=event->location.y;
  455.         if (ix3<ix2) { t=ix3; ix3=ix2; ix2=t; }
  456.         if (iy3<iy2) { t=iy3; iy3=iy2; iy2=t; }
  457.         ix2=ix2*ix1/2.0+ix1/2.0+ix;
  458.         ix3=ix3*ix1/2.0+ix1/2.0+ix;
  459.         iy2=iy2*iy1/2.0+iy1/2.0+iy;
  460.         iy3=iy3*iy1/2.0+iy1/2.0+iy;
  461.         
  462.         if (ix2<ix3 && iy2<iy3) {
  463.             minX=ix2;
  464.             minY=iy2;
  465.             maxX=ix3;
  466.             maxY=iy3;
  467.         }
  468.         [controller setMM:minX :maxX :minY :maxY];
  469.         [self zoom:self];
  470.             break;
  471.         case NX_LMOUSEDRAGGED:
  472.         [self lockFocus];
  473.         PSsetlinewidth(0.003);
  474.         PSsetgray(1.0);
  475.         PSmoveto(ix,iy);
  476.         PSlineto(ix1,iy1);
  477.         PSlineto(ix2,iy2);
  478.         PSlineto(ix3,iy3);
  479.         PSlineto(ix,iy);
  480.         PSstroke();
  481.         ix1=mx[0]*oevent->location.x+mx[1]*event->location.y+.5*mx[2];
  482.         iy1=mx[6]*oevent->location.x+mx[7]*event->location.y+.5*mx[8];
  483.         ix2=mx[0]*event->location.x+mx[1]*event->location.y+.5*mx[2];
  484.         iy2=mx[6]*event->location.x+mx[7]*event->location.y+.5*mx[8];
  485.         ix3=mx[0]*event->location.x+mx[1]*oevent->location.y+.5*mx[2];
  486.         iy3=mx[6]*event->location.x+mx[7]*oevent->location.y+.5*mx[8];
  487.         PSsetgray(0.0);
  488.         PSmoveto(ix,iy);
  489.         PSlineto(ix1,iy1);
  490.         PSlineto(ix2,iy2);
  491.         PSlineto(ix3,iy3);
  492.         PSlineto(ix,iy);
  493.         PSstroke();
  494.         [self unlockFocus];
  495.         [window flushWindow];
  496.         break;
  497.     }
  498.     }
  499.     else {
  500.         switch (event->type) {
  501.         case NX_LMOUSEUP:
  502.                 loop = 0;
  503.             dchi=(event->location.x-ix2)/(float)(event->time-tm2)*10.0;
  504.         if (fabs(dchi)<.004) dchi=0.0;
  505.                 [self display];
  506.             break;
  507.         case NX_LMOUSEDRAGGED:
  508.             theta+=(event->location.y-iy)*3.0;
  509.             chi+=(event->location.x-ix)*4.0;
  510.             if (theta>M_PI/2.0) theta=M_PI/2.0;
  511.             if (theta<-M_PI/2.0) theta=-M_PI/2.0;
  512.             while (chi>2.0*M_PI) chi-=2.0*M_PI;
  513.             while (chi<0.0) chi+=2.0*M_PI;
  514.             ix2=ix;
  515.             iy2=iy;
  516.             tm2=tm;
  517.             ix=event->location.x;
  518.             iy=event->location.y;
  519.             tm=event->time;
  520.             [self display];
  521.             break;
  522.         default:
  523.             break;
  524.         }
  525.     }
  526. }
  527. [window setEventMask:oldMask];
  528. return self;
  529. }
  530.  
  531. /* function called by timer */
  532. void itstime(DPSTimedEntry entry,double now,id call)
  533. {
  534. [call step];
  535. return;
  536. }
  537.  
  538. /* do one time step */
  539. -step
  540. {
  541. if (initflag) { 
  542.     /* first time, do initialization stuff */
  543.     if (controller==nil) return self;
  544.     [controller startup:pref];
  545.     [self zoom:self];
  546.     initflag=0; 
  547. }
  548. if (dchi==0.0) return self;
  549. chi+=dchi;
  550. while (chi>2.0*M_PI) chi-=2.0*M_PI;
  551. while (chi<0.0) chi+=2.0*M_PI;
  552. [self display];
  553. return self;
  554. }
  555.  
  556. /* set mouse mode */
  557. -setMode:sender
  558. {
  559. zMode=[[sender selectedCell] tag];
  560. return self;
  561. }
  562.  
  563. /* new min/max values */
  564. -zoomTo:(float)minx :(float)miny :(float)maxx :(float)maxy
  565. {
  566. minX=minx;
  567. minY=miny;
  568. maxX=maxx;
  569. maxY=maxy;
  570. return self;
  571. }
  572.  
  573. -(int)acceptsFirstMouse { return (YES); }
  574.  
  575. -setcontroller:con 
  576. {
  577. controller=con;
  578. return self;
  579. }
  580.  
  581. /* allow user to pause spinning */
  582. -togFreeze:sender
  583. {
  584. static float Tdchi;
  585.  
  586. if ([sender intValue]) {
  587.     Tdchi=dchi;
  588.     dchi=0.0;
  589.     return self;
  590. }
  591. dchi=Tdchi;
  592. return self;
  593. }
  594. @end
  595.